package com.utility.project.common.base;


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.utility.project.common.constants.HttpCodeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

/**
 * @author wangjiyun
 * @date 2022/12/22 07:42
 * @description 全局通用的数据返回对象 response
 */
@Data
@Schema(description = "Response object for some operation")
public class R<T> {

    /** 响应结果成功 */
    public static final int SUCCESS = HttpCodeEnum.OK.getCode();

    private static String SUCCESS_FLAG = "success";

    private static String ERROR_FLAG = "error";

    /** 响应结果失败 */
    public static final int FAIL = HttpCodeEnum.INTERNAL_SERVER_ERROR.getCode();

    @Schema(description = "返回code码")
    private int code;

    @Schema(description = "数据总数")
    private long total;

    @Schema(description = "消息提示")
    private String tooltip;

    @Schema(description = "请求状态")
    private String status;

    @Schema(description = "数据返回实体")
    private T data;

    public static <T> R<T> ok() {
        return restResult(null, SUCCESS, null);
    }

    public static <T> R<T> ok(T data) {
        return restResult(data, SUCCESS, null);
    }

    public static <T> R<List<T>> pageData(Page<T> data) {
        return pageResult(data, SUCCESS, null);
    }

    public static <T> R<T> ok(T data, String msg) {
        return restResult(data, SUCCESS, msg);
    }

    /**
     * 成功并携带指定message
     * 
     * @author wangjiyun
     * @date 2023/2/21 14:51
     */
    public static <T> R<T> okWithMsg(String msg) {
        return restResult(null, SUCCESS, msg);
    }

    public static <T> R<T> fail() {
        return restResult(null, FAIL, null);
    }

    public static <T> R<T> fail(String msg) {
        return restResult(null, FAIL, msg);
    }

    public static <T> R<T> fail(T data) {
        return restResult(data, FAIL, null);
    }

    public static <T> R<T> fail(HttpCodeEnum codeEnum) {
        return restResult(null, codeEnum.getCode(), codeEnum.getMessage());
    }

    public static <T> R<T> fail(T data, String msg) {
        return restResult(data, FAIL, msg);
    }

    public static <T> R<T> fail(int code, String msg) {
        return restResult(null, code, msg);
    }

    private static <T> R<List<T>> pageResult(Page<T> page, int code, String msg) {
        R<List<T>> apiResult = new R<>();
        apiResult.setCode(code);
        if(Objects.equals(code, FAIL)){
            apiResult.setStatus(ERROR_FLAG);
        }else{
            apiResult.setStatus(SUCCESS_FLAG);
        }
        apiResult.setTotal(page.getTotal());
        apiResult.setData(page.getRecords());
        apiResult.setTooltip(msg);
        return apiResult;
    }

    private static <T> R<T> restResult(T data, int code, String msg) {
        R<T> apiResult = new R<>();
        apiResult.setCode(code);
        if(Objects.equals(code, FAIL)){
            apiResult.setStatus(ERROR_FLAG);
        }else{
            apiResult.setStatus(SUCCESS_FLAG);
        }
        apiResult.setData(data);
        apiResult.setTooltip(msg);
        return apiResult;
    }

    public static R<Boolean> auto(boolean flag) {
        return flag ? R.ok() : R.fail();
    }

    /***
     * 判断是否成功
     * 
     * @author zouke
     * @date 2023/2/23 20:44
     */
    public static boolean isOk(R<?> r) {
        return r != null && HttpCodeEnum.OK.getCode() == r.getCode();
    }

    /***
     * 判断结果是否非空
     * 
     * @author zouke
     * @date 2023/2/23 20:44
     */
    public static boolean rsIsNotNull(R<?> r) {
        if (r == null) {
            return false;
        }
        return r.getData() != null;
    }

    /***
     * 获取结果数据
     * 
     * @author zouke
     * @date 2023/2/23 20:44
     */
    public static <T> T getRsData(R<T> r) {
        if (r == null) {
            return null;
        }
        return r.getData();
    }

    @SneakyThrows
    public static <T> T getSynData(Supplier<R> supplier){
        CompletableFuture<R> future = CompletableFuture.supplyAsync(supplier);
        R r = future.get();
        return (T) getRsData(r);
    }
}
